<?php
/**
* @package mosRest
* @author Chad Auld and Ozgur Cem Sen (code@brilaps.com)
* @copyright Brilaps, LLC (http://brilaps.com)
* @link http://brilaps.com || http://wiki.brilaps.com
* @license http://www.opensource.org/licenses/gpl-license.php GNU/GPL v.2.
*/

/** ensure this file is being included by a parent file */
defined( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' );

//unfortunately!
if (phpversion() < 5) {
    die( 'REST API requires PHP5 or later.' );
}

//Fix to allow compatibility with J!
if (!function_exists( 'T_' )) { function T_($string) {return $string;} }

require_once($mainframe->getPath('front_html', 'com_rest'));
require_once($mainframe->getPath('class'));

switch( $task ) {
	case "register":
	case "reviseRegistration":
	registerForm( $option );
	break;

	case "confirmRegistration":
	confirmRegistration( $option );
	break;

	case "saveRegistration":
	saveRegistration( $option );
	break;

	case "activate":
	activate( $option );
	break;
	
	case "lostKey":
	lostKeyForm( $option );
	break;

	case "sendNewKey":
	sendNewKey( $option );
	break;

	case "keyReset":
	keyResetRedoActivation( $option );
	break;
	
	case "versioncheck":
	checkAPIVersion( $option );
	break;
	
	default:
	die(T_('Invalid request! Please see the REST API documentation.'));
	break;
}

/**
* Generate API key rest form
* @param option - component option to call
*/
function lostKeyForm($option) {
	global $mainframe;
	$mainframe->SetPageTitle(T_('Lost your API key?'));
	HTML_registration::lostKeyForm($option);
}

/**
* Returns version number of the API.
* @param option
*/
function checkAPIVersion($option) {
    $appId = trim(mosGetParam($_GET, 'appid', ''));	
    $returnFormat = trim(mosGetParam( $_REQUEST, 'output', ''));
    $validReturnFormat = array('json', 'xml', 'php');
    
    $mRest = new mRest();
    $uri = $mRest->full_url();
    
    //Validate we have a properly formed request
	$htmlReturnStatusCode = 200; //Start as OK
	if (!in_array($returnFormat, $validReturnFormat)) {
		$htmlReturnStatusCode = 400;
		$errorMessage = T_('Invaid output format!');
		$data = $mRest->restError($errorMessage, 'json');
	} else if ($mRest->verifyRestAPIKey($appId, 'versioncheck', $uri)===false) {
		$htmlReturnStatusCode = 401;
		$errorMessage = T_('Invalid or blocked appid used in request!');
		$data = $mRest->restError($errorMessage, $returnFormat);
	} else {
	    $data = $mRest->getAPIVersion($returnFormat);
	}
    
	$mRest->restResponse($htmlReturnStatusCode, $returnFormat, $data);
}

/**
* Performs the actual reset on a valid REST account.  Sets up account for reactivation.
* @param option - component option to call
*/
function keyResetRedoActivation($option) {
	global $database, $mosConfig_mailfrom, $mosConfig_fromname, $mosConfig_live_site, $mosConfig_sitename;
	
	$resetCode = $database->getEscaped(trim(mosGetParam( $_REQUEST, 'reset_code', '')));
	$contactEmail = trim(mosGetParam($_REQUEST, 'contact_email', ''));
	$dbContactEmail = $database->getEscaped($contactEmail);
	$activationCode = md5(mosMakePassword());
	$database->setQuery( "UPDATE #__rest SET block='1', reset_code='', activation='{$activationCode}', rest_key=''"
						."\n WHERE reset_code='{$resetCode}' AND contact_email='{$dbContactEmail}'");
	if (!$database->query()) {
		echo "SQL error" . $database->stderr(true);
	} else {
		$subject = sprintf (T_('%s :: API account rest'), $mosConfig_sitename);
		$subject = html_entity_decode($subject, ENT_QUOTES);
		$activation_url = $mosConfig_live_site.'/index.php?option=com_rest&task=activate&activation='.$activationCode;
		$message = sprintf (T_('Your API account at %s has been reset as requested.  Click the link below to activate your new key.  Upon successful activation you will be sent an email with your new API key.  You will need to update your client applications with this new information before you can access the API.
	
%s'), $mosConfig_sitename, $activation_url);
		$message = html_entity_decode($message, ENT_QUOTES);
		mosMail($mosConfig_mailfrom, $mosConfig_fromname, $contactEmail, $subject, $message);
		
		echo '<h2 class="componentheading">'.T_('API Reset Complete').'</h2>';
		?>
		<p><?php echo T_('The API account has been reset as requested.  An email has been sent to the registered contact email address with an activation link.  Upon successful activation a follow-up email will be sent with the new API key.  You will need to update your client applications with this new information before you can access the API.');?></p>
		<?php
	}
}

/**
* Generates a reset request and emails the account contact for confirmation
* @param option - component option to call
*/
function sendNewKey($option) {
	global $database, $mosConfig_live_site, $mosConfig_sitename, $mosConfig_mailfrom, $mosConfig_fromname;

	$confirmEmail = trim(mosGetParam($_POST, 'confirmEmail', ''));
	$dbConfirmEmail = $database->getEscaped($confirmEmail);
	$database->setQuery( "SELECT id FROM #__rest"
	. "\n WHERE contact_email='{$dbConfirmEmail}'");

	if (!$confirmEmail || !($restID = $database->loadResult())) {
		mosRedirect( "index.php?option=com_rest&task=lostKey&mosmsg=".T_('Sorry, no corresponding record was found.  Please make sure you entered the original contact email address used to register for API access.') );
		exit;
	}
	
	//Generate a reset code and update the db
	$restResetCode = sha1(mosMakePassword());
	$sql = "UPDATE #__rest SET reset_code='$restResetCode' WHERE id='{$restID}'";
	$database->setQuery( $sql );
	if (!$database->query()) {
		die($database->stderr(true));
	}
	
	$subject = sprintf(T_('%s :: API reset request for - %s'), $mosConfig_sitename, $confirmEmail);
	$restResetUrl = $mosConfig_live_site.'/index.php?option=com_rest&task=keyReset&reset_code='.$restResetCode.'&contact_email='.$confirmEmail;
	$message = sprintf(T_("A web user from %s has just requested that a new API key be generated for this email address.  If you did in fact make this request please click on the link below to verify the request.\n\n
%s\n\n
If you didn't ask for this, don't worry. You are seeing this message, not them.  Simply ignore this message an no further action will be taken."), $mosConfig_live_site, $restResetUrl);
	$message = html_entity_decode($message, ENT_QUOTES);
	mosMail($mosConfig_mailfrom, $mosConfig_fromname, $confirmEmail, $subject, $message);
		mosRedirect( "index.php?option=com_rest&task=lostKey&mosmsg=".T_('An API reset request has been sent to the contact email address!') );
}

/**
* Generates the REST API new user registration form
* @param option - component option to call
*/
function registerForm($option) {
	global $mainframe, $mosConfig_absolute_path;
	
	//Include REST settings
	$allow_rest_registration = '';
	require($mosConfig_absolute_path.'/administrator/components/com_rest/config.rest.php');
	if ($allow_rest_registration=='0') {
		mosNotAuth();
		return;
	} else {
		$mainframe->SetPageTitle(T_('Registration'));
		HTML_registration::registerForm($option);
	}
}

/**
* Generates the REST API new user registration confirmation form
* Also performs validation of user provided details
* @param option - component option to call
*/
function confirmRegistration ($option) {
	global $mainframe, $mosConfig_absolute_path;
	
	//Include base REST settings
	$show_rest_registration_disclaimer = '';
	require($mosConfig_absolute_path."/administrator/components/com_rest/config.rest.php");
		
	$developer_name = trim(mosGetParam( $_REQUEST, 'developer_name', ''));
	$product_name   = trim(mosGetParam( $_REQUEST, 'product_name', ''));
	$web_app_url    = trim(mosGetParam( $_REQUEST, 'web_app_url', ''));
	$contact_email  = trim(mosGetParam( $_REQUEST, 'contact_email', ''));
	$phone_number   = trim(mosGetParam( $_REQUEST, 'phone_number', ''));
	$description    = trim(mosGetParam( $_REQUEST, 'description', ''));
	$accept         = trim(mosGetParam( $_REQUEST, 'accept', 'no'));

	/* Preform validations and send them back to the registration form with prev post info so they can correct if needed.  
		Using sessions in place of javascript history for accessibility. */
	if (!$developer_name || strlen($developer_name)>100) { //check developer name	
		$reg_error_text = sprintf(T_('Please enter a developer name.  There is a %d character maximum length.'),100);
		$_SESSION['reg_error_type'] = 'developer_name';
		$_SESSION['reg_error_text'] = $reg_error_text;
	} else if (!$product_name || strlen($product_name)>100) { //check product name	
		$reg_error_text = sprintf(T_('Please specify a product name.  There is a %d character maximum length.'),100);
		$_SESSION['reg_error_type'] = 'product_name';
		$_SESSION['reg_error_text'] = $reg_error_text;
	} else if (strlen($web_app_url)>500) { //check web app url	
		$reg_error_text = sprintf(T_('Please check the web application url.  There is a %d character maximum length.'),500);
		$_SESSION['reg_error_type'] = 'web_app_url';
		$_SESSION['reg_error_text'] = $reg_error_text;
	} else if (!is_email($contact_email) || strlen($contact_email)>100) { //check email
		$reg_error_text = T_('Please enter a valid e-mail address.');
		$_SESSION['reg_error_type'] = 'contact_email';
		$_SESSION['reg_error_text'] = $reg_error_text;	
	} else if (strlen($phone_number)>25) { //check phone number
		$reg_error_text = sprintf(T_('Please check the phone number.  There is a %d character maximum length.'),25);
		$_SESSION['reg_error_type'] = 'phone_number';
		$_SESSION['reg_error_text'] = $reg_error_text;
	} else if (!$description || strlen($description)>255) { //check description	
		$reg_error_text = sprintf(T_('Please enter a decription for how you intend to use the API.  There is a %d character maximum length.'),255);
		$_SESSION['reg_error_type'] = 'description';
		$_SESSION['reg_error_text'] = $reg_error_text;
	} elseif ($show_rest_registration_disclaimer=='1' && $accept!='yes') { //Force them to accept if set globally
		$reg_error_text = T_('You must accept the Privacy Policy and Disclaimer, to continue.');
		$_SESSION['reg_error_type'] = 'accept';
		$_SESSION['reg_error_text'] = $reg_error_text;
	}
	
	/* Setup session variables to hold the previously posted data.  Use these to restore the form 
	if not valid or	in the case that the user comes back to correct their information during regisitration. */
	$_SESSION['reg_developer_name'] = $developer_name;
	$_SESSION['reg_product_name'] = $product_name;
	$_SESSION['reg_web_app_url'] = $web_app_url;
	$_SESSION['reg_contact_email'] = $contact_email;
	$_SESSION['reg_phone_number'] = $phone_number;
	$_SESSION['reg_description'] = $description;
	$_SESSION['reg_accept'] = $accept;
		
	//Do we have any errors?  Send back if so...
	$reg_error_type = mosGetParam( $_SESSION, 'reg_error_type', '' );
	if ($reg_error_type!='') {	
		//Send them back to the form		
		HTML_registration::registerForm($option);
	} else {
		//If we are here then the form was valid so forward them to the confirmation form
		HTML_registration::confirmForm($option, $developer_name, $product_name, $web_app_url, $contact_email, $phone_number, $description);
	}
}

/**
* Saves API regisitration details and notifies administrators of the request for approval
* @param option - component option to call
*/
function saveRegistration( $option ) {
	global $database, $my, $acl;
	global $mosConfig_sitename, $mosConfig_live_site, $mosConfig_mailfrom, $mosConfig_fromname, $mosConfig_absolute_path;
	
	//Include REST settings
	$allow_rest_registration = '';
	require($mosConfig_absolute_path.'/administrator/components/com_rest/config.rest.php');;
	if ($allow_rest_registration=='0') {
		mosNotAuth();
		return;
	}
	
	$developer_name = trim(mosGetParam($_REQUEST, 'developer_name', ''));
	$product_name   = trim(mosGetParam($_REQUEST, 'product_name', ''));;
	$web_app_url    = trim(mosGetParam($_REQUEST, 'web_app_url', ''));
	$contact_email  = trim(mosGetParam($_REQUEST, 'contact_email', ''));
	$phone_number   = trim(mosGetParam($_REQUEST, 'phone_number', ''));
	$description    = trim(mosGetParam($_REQUEST, 'description', ''));

	$row = new mosRest( $database );
	$row->id = 0;
	$row->developer_name = $database->getEscaped($developer_name);
	$row->product_name = $database->getEscaped($product_name);
	$row->web_app_url = $database->getEscaped($web_app_url);
	$row->contact_email = $database->getEscaped($contact_email);
	$row->phone_number = $database->getEscaped($phone_number);
	$row->description = $database->getEscaped($description);
	$row->activation = md5(mosMakePassword());
	$row->block = '1';
	$row->registration_date = date("Y-m-d H:i:s");
	
	if (!$row->store()) {
		echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n";
		exit();
	}

	$subject = sprintf (T_('API account details for %s at %s'), $developer_name, $mosConfig_sitename);
	$message = sprintf (T_('Hello %s,

Thank you for registering for API access at %s. Your request has been sent to the site administrator.  As a precaution, it must be approved and activated before you can use it.  A follow-up email will be sent to you once the administrator has approved your request.'), 
	$developer_name, $mosConfig_sitename);
	
	$subject = html_entity_decode($subject, ENT_QUOTES);
	$message = html_entity_decode($message, ENT_QUOTES);
	//Send message
	if (!mosMail($mosConfig_mailfrom, $mosConfig_fromname, $contact_email, $subject, $message)) {
		echo 'error';
	}

	//Compose a message for the administrators
	$subject2 = sprintf (T_('API request for %s at %s'), $developer_name, $mosConfig_sitename);
	$message2 = sprintf (T_('Hello Administrator,

%s has registered for API access at %s.
This email contains the request details:

Developer Name - %s
Product Name - %s
Web Application URL - %s
E-mail - %s
Phone Number - %s
Description - %s

You will need to approve this request in the REST Component before the user can utilize the API service.

Please do not respond to this message as it is automatically generated and is for information purposes only.'), 
	$developer_name, $mosConfig_sitename, $developer_name, $product_name, $web_app_url, 
	$contact_email, $phone_number, $description);
	$subject2 = html_entity_decode($subject2, ENT_QUOTES);
	$message2 = html_entity_decode($message2, ENT_QUOTES);

	//Get superadministrator ids
	$admins = $acl->get_group_objects( 25, 'ARO' );
	//Send notification to each administrator that is set to get mail
	foreach ( $admins['users'] AS $id ) {
		$database->setQuery( "SELECT email, sendEmail FROM #__users"
							."\n WHERE id='{$id}'" );
		$rows = $database->loadObjectList();
		$row = $rows[0];
		if ($row->sendEmail) {
			mosMail($mosConfig_mailfrom, $mosConfig_fromname, $row->email, $subject2, $message2);
		}
	}

	echo '<h2 class="componentheading">'.T_('API Registration Complete').'</h2>';
	?>
	<p><?php echo T_('Thank you for registering for API access. Your request has been sent to the site administrator.  As a precaution, it must be approved and activated before you can use it.  A follow-up email will be sent to you once the administrator has approved your request.');?></p>
	<?php
	
}

/**
* Handles the actual activation of the API account and sends user API key
* @param option - component option to call
*/
function activate( $option ) {
	global $database, $mosConfig_sitename, $mosConfig_mailfrom, $mosConfig_fromname, $mosConfig_absolute_path;
	
	//Include REST settings
	$allow_rest_registration = '';
	require($mosConfig_absolute_path.'/administrator/components/com_rest/config.rest.php');
	if ($allow_rest_registration=='0') {
		mosNotAuth();
		return;
	}

	$activation = $database->getEscaped(trim(mosGetParam($_REQUEST, 'activation', '')));
	if (empty( $activation )) {
        echo '<div class="componentheading">'.T_('Invalid Activation Link!').'</div><br />';
		echo T_('There is no such account in our database or the account has already been activated.');
		return;
	}
	
	$database->setQuery( "SELECT id, developer_name, contact_email FROM #__rest"
						."\n WHERE activation='{$activation}'"
						."\n AND approval_date<=now()"
						."\n AND approval_date IS NOT NULL" );
	$row = $database->loadObjectList();
	if ($row) {
		$restID = $row[0]->id;
		$developer_name = $row[0]->developer_name;
		$contact_email = $row[0]->contact_email;
		
		//Generate the REST API key
		$restKey = '';
		do {
		    $restKey = sha1(mosMakePassword());
		} while (!checkUniqueRestKey($restKey)); //must be unique
		
		$database->setQuery( "UPDATE #__rest SET block='0', activation='', rest_key='{$restKey}'"
							."\n WHERE activation='{$activation}' AND block='1'"
							."\n AND id={$restID}");
		if (!$database->query()) {
			echo $database->stderr(true);
		} else {
			$subject = sprintf (T_('API account approved for %s'), $mosConfig_sitename);
			$message = sprintf (T_('Hello %s,

Thank you for registering for RESTful API access at %s. Your request has been approved by the site administrator.  Below you will find the REST API key needed for access.  These can be used with the native REST component.  More information on the REST component can be found here - http://wiki.brilaps.com/wikka.php?wakka=MOStlyREST.

REST API Key - %s'), $developer_name, $mosConfig_sitename, $restKey);
			$subject = html_entity_decode($subject, ENT_QUOTES);
			$message = html_entity_decode($message, ENT_QUOTES);
			//Send email
			mosMail($mosConfig_mailfrom, $mosConfig_fromname, $contact_email, $subject, $message);
			
			echo '<div class="componentheading">'.T_('Activation Complete!').'</div><br />';
			echo T_('Your RESTful API account has been activated successfully. A new email has been sent to you with your API login information.');
		}
	} else {
	    echo '<div class="componentheading">'.T_('Invalid Activation Link!').'</div><br />';
		echo T_('Either there is no such account in our database, the account has yet to be approved, or the account has already been activated.');
	}
}

/**
* Used to verify the that the generated API key is unique
* @param restKey - The key to compare
*/
function checkUniqueRestKey($restKey) {
	global $database;
	
	$database->setQuery( "SELECT count(id) as key_count FROM #__rest"
						."\n WHERE rest_key='{$restKey}'" );
	$key_count = $database->loadResult();
	if ($key_count>0) {
		return false;
	} else {
		return true;
	}
}

/**
* Check field contains an email address
* Returns false if text is not an email address
*/
function is_email($email){
	$rBool=false;

	if(preg_match("/[\w\.\-]+@\w+[\w\.\-]*?\.\w{1,4}/", $email)){
		$rBool=true;
	}
	return $rBool;
}

?>